pub use pest::{Parser, pratt_parser::*, iterators::*};
pub use pest_derive::Parser;
use std::fs;
use std::env;
use std::io;
use std::collections::HashMap;
use std::io::BufRead;
use std::path::{Path, PathBuf};


#[derive(Parser)]
#[grammar = "calculator.pest"]
pub struct CalcuParser;

/**
 * Pratt Parser : 自上而下操作符优先级解析法（Top Down Operator Precedence Parsing）
 * 
 * 基本思路是：每当解析一个表达式时，解析器会根据当前读取的内容判断它的类型——是普通元素还是操作符，
 * 并据此决定是否需要继续深入解析。
 * 依赖于一个核心机制：每个操作符都被赋予一个优先级数值，
 * 解析器根据这个数值决定是否应该提前结束当前解析阶段。
 * 换句话说，Pratt Parser 是通过动态调整期望看到的操作符优先级的方式，来控制解析深度的。
 * 例如，解析二元运算时，在进入右侧解析前，它会比较当前操作符（如+）的绑定强度与某个预期值的大小关系。
 * 如果后续操作符（如*）的优先级更高，则允许继续深入解析；反之则立即返回结果。
 */

fn parse_expr(pairs: Pairs<Rule>, pratt: &PrattParser<Rule>) -> i32 {
    pratt
        .map_primary(|primary| match primary.as_rule() {
            Rule::int  => primary.as_str().parse().unwrap(),
            Rule::expr => parse_expr(primary.into_inner(), pratt), // from "(" ~ expr ~ ")"
            _          => unreachable!(),
        })
        .map_prefix(|op, rhs| match op.as_rule() {
            Rule::neg  => -rhs,
            _          => unreachable!(),
        })
        .map_postfix(|lhs, op| match op.as_rule() {
            Rule::fac  => (1..lhs+1).product(),
            _          => unreachable!(),
        })
        .map_infix(|lhs, op, rhs| match op.as_rule() {
            Rule::add  => lhs + rhs,
            Rule::sub  => lhs - rhs,
            Rule::mul  => lhs * rhs,
            Rule::div  => lhs / rhs,
            Rule::pow  => (1..rhs+1).map(|_| lhs).product(),
            _          => unreachable!(),
        })
        .parse(pairs)
}

pub fn test() {
    let pratt =
    PrattParser::new()
        .op(Op::infix(Rule::add, Assoc::Left) | Op::infix(Rule::sub, Assoc::Left))
        .op(Op::infix(Rule::mul, Assoc::Left) | Op::infix(Rule::div, Assoc::Left))
        .op(Op::infix(Rule::pow, Assoc::Right))
        .op(Op::postfix(Rule::fac))
        .op(Op::prefix(Rule::neg));

    for line in io::stdin().lock().lines() {
        match CalcuParser::parse(Rule::expr, &line.unwrap()) {
            Ok(mut pairs) => {
                println!(
                    "Parsed: {:#?}",
                    parse_expr(pairs.next().unwrap().into_inner(), &pratt)
                );
            }
            Err(e) => {
                eprintln!("Parse failed: {:?}", e);
            }
        }
    }
    println!("pratt-parser done");
}